package com.gitee.wsl.security.hash.bcrypt

import com.gitee.wsl.lang.bytearray.Bytes
import com.gitee.wsl.lang.bytearray.BytesTransformer
import com.gitee.wsl.security.hash.sha512


/**
 * Strategy if the password is longer than supported by Bcrypt itself (71 bytes + null terminator with $2a$)
 */
interface LongPasswordStrategy {
    /**
     * Derives (hashes, shortens, etc) the given password to a desired max length.
     *
     * @param rawPassword to check and derive
     * @return the derived (shortened) password, or the same reference if short enough
     */
    fun derive(rawPassword: ByteArray): ByteArray

    /**
     * Default base implementation
     */
    abstract class BaseLongPasswordStrategy(val maxLength: Int) : LongPasswordStrategy {
        abstract fun innerDerive(input: ByteArray): ByteArray

        override fun derive(rawPassword: ByteArray): ByteArray {
            if (rawPassword.size >= maxLength) {
                return innerDerive(rawPassword)
            }
            return rawPassword
        }
    }

    /**
     * This strategy will always throw an exception to force passwords under the max length
     */
    class StrictMaxPasswordLengthStrategy internal constructor(maxLength: Int) : BaseLongPasswordStrategy(maxLength) {

         override fun innerDerive(rawPassword: ByteArray): ByteArray {
            throw IllegalArgumentException("password must not be longer than " + maxLength + " bytes plus null terminator encoded in utf-8, was " + rawPassword.size)
        }
    }

    /**
     * Will use sha512 to hash given password to generate fixed 64 byte length hash value
     */
    class Sha512DerivationStrategy internal constructor(maxLength: Int) : BaseLongPasswordStrategy(maxLength) {

          override fun innerDerive(rawPassword: ByteArray): ByteArray {
              return rawPassword.sha512().bytes
            //return Bytes.wrap(rawPassword).hash("SHA-512").array()
        }
    }

    /**
     * Truncates the password the max possible length.
     *
     *
     * NOTE: This is not recommended, only for compatibility with current hashes; uses [Sha512DerivationStrategy]
     * if you need to support passwords with arbitrary lengths.
     */
    class TruncateStrategy internal constructor(maxLength: Int) : BaseLongPasswordStrategy(maxLength) {

        override fun innerDerive(rawPassword: ByteArray): ByteArray {
            return Bytes.wrap(rawPassword)
                .resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array()
        }
    }

    /**
     * A simple strategy that just returns the provided password without changing it.
     */
    class PassThroughStrategy : LongPasswordStrategy {
        override fun derive(rawPassword: ByteArray): ByteArray{
            return rawPassword
        }
    }
}
